/*
 * Copyright (C) 2007 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.util;

/**
 * 容器:为动态类型的数据值。 主要用于{@link android.content.res.Resources} 保存资源值。
 */
public class TypedValue {
    /** 该值不包含数据。 */
    public static final int TYPE_NULL = 0x00;

    /**数据字段包含资源标识符。
     * type reference
     * */
    public static final int TYPE_REFERENCE = 0x01;
    /**
     * 数据字段包含属性资源标识符（引用当前主题样式中的属性，而不是资源条目）。
     * type attribute
     *  */
    public static final int TYPE_ATTRIBUTE = 0x02;
    /** 字符串字段包含字符串数据。 另外，如果数据不为零，那么它是字符串的字符串块索引，而资产Cookie是字符串来自的资产集合。
     *  type string
     *  */
    public static final int TYPE_STRING = 0x03;
    /** 数据字段包含IEEE 754浮点数。
     * type float
     * */
    public static final int TYPE_FLOAT = 0x04;
    /**
     *  数据字段包含一个dimension值的复数。
     *  type dimension
     *
     *  */
    public static final int TYPE_DIMENSION = 0x05;
    /**
     *  数据字段包含一个fraction容器的复数
     *  type fraction
     *  */
    public static final int TYPE_FRACTION = 0x06;

    /**
     * 标识普通整数值的开始。 从此到{@link #TYPE_LAST_INT} 的任何类型值意味着数据字段包含一个通用整数值。
     *
     *  type_first_int
     *  */
    public static final int TYPE_FIRST_INT = 0x10;

    /** 数据字段包含最初以十进制指定的数字 */
    public static final int TYPE_INT_DEC = 0x10;
    /** 数据字段包含最初以十六进制（0xn）指定的数字*/
    public static final int TYPE_INT_HEX = 0x11;
    /** 数据字段包含最初指定为“false”或“true”的0或1。*/
    public static final int TYPE_INT_BOOLEAN = 0x12;

    /** 标识指定为颜色常量的整数值的开始（以'＃'开头）。*/
    public static final int TYPE_FIRST_COLOR_INT = 0x1c;

    /** 数据字段保存最初指定为#aarrggbb的颜色。*/
    public static final int TYPE_INT_COLOR_ARGB8 = 0x1c;
    /** 数据字段保存最初指定为#rrggbb的颜色。*/
    public static final int TYPE_INT_COLOR_RGB8 = 0x1d;
    /** 数据字段保存最初指定为#argb的颜色。 */
    public static final int TYPE_INT_COLOR_ARGB4 = 0x1e;
    /** 数据字段保存最初指定为#rgb的颜色。*/
    public static final int TYPE_INT_COLOR_RGB4 = 0x1f;

    /** 标识指定为颜色常量的整数值的结束*/
    public static final int TYPE_LAST_COLOR_INT = 0x1f;

    /** 标识普通整数值的结尾. */
    public static final int TYPE_LAST_INT = 0x1f;

    /* ------------------------------------------------------------ */

    /** ：复杂数据：单元信息的bit位置 */
    public static final int COMPLEX_UNIT_SHIFT = 0;
    /**
     *  复杂数据：用于提取单位信息的掩码（在通过{@link #COMPLEX_UNIT_SHIFT}移位后）。 这给了我们16种可能的类型，如下所定义。
     *  */
    public static final int COMPLEX_UNIT_MASK = 0xf;

    /** {@link #TYPE_DIMENSION} 复杂单位：值是原始像素 */
    public static final int COMPLEX_UNIT_PX = 0;
    /** {@link #TYPE_DIMENSION} 复杂单位：值是与设备无关的像素 */
    public static final int COMPLEX_UNIT_DIP = 1;
    /** {@link #TYPE_DIMENSION} 复杂单位：值是缩放的像素。 */
    public static final int COMPLEX_UNIT_SP = 2;
    /** {@link #TYPE_DIMENSION} 复杂单位：值以分数表示*/
    public static final int COMPLEX_UNIT_PT = 3;
    /** {@link #TYPE_DIMENSION} 复杂单位：值以英寸为单位*/
    public static final int COMPLEX_UNIT_IN = 4;
    /** {@link #TYPE_DIMENSION} 杂单位：值以毫米为单位。*/
    public static final int COMPLEX_UNIT_MM = 5;

    /** {@link #TYPE_FRACTION} 复杂单位：整体大小的基本部分。 fraction:分数；部分；小部分；稍微 */
    public static final int COMPLEX_UNIT_FRACTION = 0;
    /** {@link #TYPE_FRACTION} 复杂单位：父级大小的一部分。*/
    public static final int COMPLEX_UNIT_FRACTION_PARENT = 1;

    /** 复杂数据：基数信息在哪里，告诉小数位在尾数中出现的位置。*/
    public static final int COMPLEX_RADIX_SHIFT = 4;
    /**复杂数据：用于提取基数信息的掩码（在通过{@link #COMPLEX_RADIX_SHIFT}移位之后）。 这给了我们以下定义的4个可能的定点表示。*/
    public static final int COMPLEX_RADIX_MASK = 0x3;

    /** 复杂数据：尾数是一个整数 -- i.e., 0xnnnnnn.0 */
    public static final int COMPLEX_RADIX_23p0 = 0;
    /** 复杂数据：尾数幅度是16位 -- i.e, 0xnnnn.nn */
    public static final int COMPLEX_RADIX_16p7 = 1;
    /** 复杂数据：尾数大小是8位 -- i.e, 0xnn.nnnn */
    public static final int COMPLEX_RADIX_8p15 = 2;
    /** 复杂数据：尾数量级为0位 -- i.e, 0x0.nnnnnn */
    public static final int COMPLEX_RADIX_0p23 = 3;

    /** 复杂数据：尾数信息的bit位置。 */
    public static final int COMPLEX_MANTISSA_SHIFT = 8;
    /**
     * 复杂数据：用于提取尾数信息的掩码（在通过{@link #COMPLEX_MANTISSA_SHIFT}移位之后）。 这给我们23位的精度; 最高位是标志。 */
    public static final int COMPLEX_MANTISSA_MASK = 0xffffff;

    /* ------------------------------------------------------------ */

    /**
     * 如果{@link #density}等于此值，那么应将密度视为系统的默认密度值：{@link DisplayMetrics#DENSITY_DEFAULT}。
     */
    public static final int DENSITY_DEFAULT = 0;

    /**
     * 如果{@link #density}等于这个值，那么没有与资源相关的密度，它不应该被缩放。
     */
    public static final int DENSITY_NONE = 0xffff;

    /* ------------------------------------------------------------ */

    /** 这个值由这个常量定义的类型。 这告诉你如何解释对象中的其他字段 */
    public int type;

    /** 如果该值包含一个字符串，就是这样。*/
    public CharSequence string;

    /** 该值中的基本数据，根据 {@link #type} 进行解释*/
    public int data;

    /** 有关值来自何处的附加信息; 只为字符串设置。 */
    public int assetCookie;

    /** 如果Value来自资源，则它保存相应的资源ID。*/
    public int resourceId;

    /** 果Value来自资源，则这些是其内容可以更改的配置 */
    public int changingConfigurations = -1;

    /**
     * 如果该值来自资源，则它将保留相应的像素密度。
     * */
    public int density;

    /* ------------------------------------------------------------ */

    /**
     * 以浮点形式返回此值的数据。 仅用于类型为{@link #TYPE_FLOAT}的值。*/
    public final float getFloat() {
        return Float.intBitsToFloat(data);
    }

    private static final float MANTISSA_MULT =
        1.0f / (1<<TypedValue.COMPLEX_MANTISSA_SHIFT);
    private static final float[] RADIX_MULTS = new float[] {
        1.0f*MANTISSA_MULT, 1.0f/(1<<7)*MANTISSA_MULT,
        1.0f/(1<<15)*MANTISSA_MULT, 1.0f/(1<<23)*MANTISSA_MULT
    };

    /**
     * 从复杂的数据整数中检索基本值。 这使用数据的{@link #COMPLEX_MANTISSA_MASK}
     * 和{@link #COMPLEX_RADIX_MASK} 字段来计算它们描述的数字的浮点表示。 单位被忽略。
     *
     *  
     * @param complex 一个复杂的数据值。
     * 
     * @return 与复杂数据对应的浮点值。
     */
    public static float complexToFloat(int complex)
    {
        return (complex&(TypedValue.COMPLEX_MANTISSA_MASK
                   <<TypedValue.COMPLEX_MANTISSA_SHIFT))
            * RADIX_MULTS[(complex>>TypedValue.COMPLEX_RADIX_SHIFT)
                            & TypedValue.COMPLEX_RADIX_MASK];
    }

    /**
     * 将保存维度的复杂数据值转换为其最终浮点值。 给定的数据必须被构造为{@link #TYPE_DIMENSION}。
     *
     *  
     * @param data 一个复杂的数据值，包含一个单位，数量级和尾数。
     * @param metrics 当前在转换中使用的显示指标 - 提供显示密度和缩放信息。
     * 
     * @return 复数浮点值根据其单位乘以适当的度量值。
     */
    public static float complexToDimension(int data, DisplayMetrics metrics)
    {
        return applyDimension(
            (data>>COMPLEX_UNIT_SHIFT)&COMPLEX_UNIT_MASK,
            complexToFloat(data),
            metrics);
    }

    /**
     * 将保存维度的复杂数据值转换为其最终值作为整数像素偏移量。 除了将原始浮点数值截断为整数（像素）值之外，
     * 它与{@link #complexToDimension}相同。 给定的数据必须被构造为{@link #TYPE_DIMENSION}.
     *
     *  
     * @param data 一个复杂的数据值，包含一个单位，数量级和尾数。
     * @param metrics 当前在转换中使用的显示指标 - 提供显示密度和缩放信息。
     * 
     * @return 数据指定的像素数量及其所需的乘数和单位。
     */
    public static int complexToDimensionPixelOffset(int data,
            DisplayMetrics metrics)
    {
        return (int)applyDimension(
                (data>>COMPLEX_UNIT_SHIFT)&COMPLEX_UNIT_MASK,
                complexToFloat(data),
                metrics);
    }

    /**
     * 将保存维度的复杂数据值转换为整数像素大小的最终值。 除了将原始浮点值转换为整数（像素）值以用作大小之外，这与{@link #complexToDimension}相同。
     * 大小转换包括舍入基值，并确保非零基值至少有一个像素大小。 给定的数据必须被构造为{@link #TYPE_DIMENSION}.
     *
     *  
     * @param data 一个复杂的数据值，包含一个单位，数量级和尾数
     * @param metrics 当前在转换中使用的显示指标 - 提供显示密度和缩放信息。
     * 
     * @return 数据指定的像素数量及其所需的乘数和单位。
     */
    public static int complexToDimensionPixelSize(int data,
            DisplayMetrics metrics)
    {
        final float value = complexToFloat(data);
        final float f = applyDimension(
                (data>>COMPLEX_UNIT_SHIFT)&COMPLEX_UNIT_MASK,
                value,
                metrics);
        final int res = (int)(f+0.5f);
        if (res != 0) return res;
        if (value == 0) return 0;
        if (value > 0) return 1;
        return -1;
    }

    /**
     * @hide 出于调试目的，意外暴露在API级别1中。 尽管调试代码已被删除，但为了防万一，仍保留兼容性。
     */
    @Deprecated
    public static float complexToDimensionNoisy(int data, DisplayMetrics metrics)
    {
        return complexToDimension(data, metrics);
    }

    /**
     * 将保存维度的解压后的复杂数据值转换为其最终浮点值。 这两个参数的单位和值如{@link #TYPE_DIMENSION}.所示。
     *
     *  
     * @param unit 要转换的单位
     * @param value 将单位应用于的值。
     * @param metrics 当前在转换中使用的显示指标 - 提供显示密度和缩放信息
     * 
     * @return 复数浮点值根据其单位乘以适当的度量值。
     */
    public static float applyDimension(int unit, float value,
                                       DisplayMetrics metrics)
    {
        switch (unit) {
        case COMPLEX_UNIT_PX:
            return value;
        case COMPLEX_UNIT_DIP:
            return value * metrics.density;
        case COMPLEX_UNIT_SP:
            return value * metrics.scaledDensity;
        case COMPLEX_UNIT_PT:
            return value * metrics.xdpi * (1.0f/72);
        case COMPLEX_UNIT_IN:
            return value * metrics.xdpi;
        case COMPLEX_UNIT_MM:
            return value * metrics.xdpi * (1.0f/25.4f);
        }
        return 0;
    }


    /**
     * 将此值的数据作为维度返回。 仅用于类型为{@link #TYPE_DIMENSION}的值。
     * 
     * @param metrics 当前在转换中使用的显示指标 - 提供显示密度和缩放信息。
     * 
     * @return 复数浮点值根据其单位乘以适当的度量值。
     */
    public float getDimension(DisplayMetrics metrics)
    {
        return complexToDimension(data, metrics);
    }

    /**
     * 将包含分数的复杂数据值转换为其最终浮点值。 给定的数据必须被构造为一个{@link #TYPE_FRACTION}。
     * .
     * 
     * @param data 一个复杂的数据值，包含一个单位，数量级和尾数。
     * @param base 这个分数的基值。 换句话说，标准分数乘以该值。
     * @param pbase 此分数的父基值。 换句话说，一个母体分数（nn％p）乘以这个值。
     * 
     * @return 复数浮点值取决于其单位乘以适当的基值
     */
    public static float complexToFraction(int data, float base, float pbase)
    {
        switch ((data>>COMPLEX_UNIT_SHIFT)&COMPLEX_UNIT_MASK) {
        case COMPLEX_UNIT_FRACTION:
            return complexToFloat(data) * base;
        case COMPLEX_UNIT_FRACTION_PARENT:
            return complexToFloat(data) * pbase;
        }
        return 0;
    }

    /**
     * 将该值的数据作为分数返回。 仅用于类型为{@link #TYPE_FRACTION}的值。
     *
     * @param base 这个分数的基值。 换句话说，标准分数乘以该值。
     * @param pbase 此分数的父基值。 换句话说，一个母体分数（nn％p）乘以这个值。
     * 
     * @return 复数浮点值取决于其单位乘以适当的基值。
     */
    public float getFraction(float base, float pbase)
    {
        return complexToFraction(data, base, pbase);
    }

    /**
     * 无论该值的实际类型如何，都尝试将其转换为字符串值。 例如，颜色类型将被转换为形式为#aarrggbb的字符串。
     * 
     * @return 字符序列被强制的字符串值。 如果值为null或者类型未知，则返回null。
     */
    public final CharSequence coerceToString()
    {
        int t = type;
        if (t == TYPE_STRING) {
            return string;
        }
        return coerceToString(t, data);
    }

    private static final String[] DIMENSION_UNIT_STRS = new String[] {
        "px", "dip", "sp", "pt", "in", "mm"
    };
    private static final String[] FRACTION_UNIT_STRS = new String[] {
        "%", "%p"
    };

    /**
     * 根据显式提供的类型和数据对{@link #coerceToString()} 执行类型转换
     *
     * 
     * @param type 数据类型标识符。
     * @param data 数据值。
     * 
     * @return String ：强制的字符串值。 如果值为null或者类型未知，则返回null。
     */
    public static final String coerceToString(int type, int data)
    {
        switch (type) {
        case TYPE_NULL:
            return null;
        case TYPE_REFERENCE:
            return "@" + data;
        case TYPE_ATTRIBUTE:
            return "?" + data;
        case TYPE_FLOAT:
            return Float.toString(Float.intBitsToFloat(data));
        case TYPE_DIMENSION:
            return Float.toString(complexToFloat(data)) + DIMENSION_UNIT_STRS[
                (data>>COMPLEX_UNIT_SHIFT)&COMPLEX_UNIT_MASK];
        case TYPE_FRACTION:
            return Float.toString(complexToFloat(data)*100) + FRACTION_UNIT_STRS[
                (data>>COMPLEX_UNIT_SHIFT)&COMPLEX_UNIT_MASK];
        case TYPE_INT_HEX:
            return "0x" + Integer.toHexString(data);
        case TYPE_INT_BOOLEAN:
            return data != 0 ? "true" : "false";
        }

        if (type >= TYPE_FIRST_COLOR_INT && type <= TYPE_LAST_COLOR_INT) {
            return "#" + Integer.toHexString(data);
        } else if (type >= TYPE_FIRST_INT && type <= TYPE_LAST_INT) {
            return Integer.toString(data);
        }

        return null;
    }

    public void setTo(TypedValue other)
    {
        type = other.type;
        string = other.string;
        data = other.data;
        assetCookie = other.assetCookie;
        resourceId = other.resourceId;
        density = other.density;
    }

    public String toString()
    {
        StringBuilder sb = new StringBuilder();
        sb.append("TypedValue{t=0x").append(Integer.toHexString(type));
        sb.append("/d=0x").append(Integer.toHexString(data));
        if (type == TYPE_STRING) {
            sb.append(" \"").append(string != null ? string : "<null>").append("\"");
        }
        if (assetCookie != 0) {
            sb.append(" a=").append(assetCookie);
        }
        if (resourceId != 0) {
            sb.append(" r=0x").append(Integer.toHexString(resourceId));
        }
        sb.append("}");
        return sb.toString();
    }
};

